/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Downloader;

//Versão 1.4
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.FileNameMap;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.regex.PatternSyntaxException;

/**
 *
 * @author gil
 */
public class CoreDownloader {

    private String fullWebPath;
    private String localPath;
    private long size;
    private URL url;
    private boolean tipo;
    private String host;
    private String fileName;
    private String path;
    private boolean windows;
    private int retornoPagina;
    private String protocol;
    private boolean nivelMax;
    private int nivel;

    public int coreDownloadFile(String url, String localPath, int nivelMax) throws MalformedURLException {

        /*
         * 1- recebe o url (string) e o localpath (string) local onde vai ser guardado o ficheiro 
         * 2- convert a string em url
         * 3- obtem o nome do ficheiro para fazer download
         * 4- full path na web
         * 5- tamanho do ficheiro
         * 6- host na web
         * 7- path local 
         */


        /*
         * retorno
         *  -1 no caso do link não estar bem formatado ou bem definido
         *  -2 no caso de erro no get response code
         *  -3 no caso do nivelMax ser superior ao definido
         *  -4 no caso do ficheiro não existir, erro 400/500
         *  -5 no caso de não conseguir gravar localmente
         *  0 tudo ok
         */

        try {
            this.url = new URL(url);
        } catch (MalformedURLException ex) {
            System.out.println("\n URL inválido, verificar se protocol existe (http / ftp.....");
//         *  -1 no caso do link não estar bem formatado ou bem definido
            return -1;
        }

        this.retornoPagina = 400;
        try {

            this.retornoPagina = CoreDownloader.getResponseCode(url);
        } catch (IOException ex) {
            System.out.println("\n Erro ao obter responseCode de " + ex.getMessage());
            //         *  -2 no caso de erro no get response code
            return -2;

        }

        if (this.retornoPagina < 400) {

            //full path na web
            this.setFullWebPath(url);

            this.verificaNivel(nivelMax);

            if (this.nivelMax) {

                verificaURL();

                //set do host/site
                this.setHost();

                //set do local path
                this.setLocalPath(localPath);
                verificaUltimoCaracterPathLocal();
                String tmp = this.getLocalPath();
                tmp += this.getHost();

                if (criaDir(tmp) > 0) {

                    //set do tamanho do ficheiro
                    this.setSize();

                    //set do protocol
                    this.setProtocol();

                    ifOnlyHost();

                    //verificar se o tipo de ficheiro é html/etc
                    this.fileType();


                    //verifica se é windows ou linux/mac
                    this.setWindows();


                    this.path = convertBarras(this.path);
                } else {
                    //-5 no caso de não conseguir gravar localmente
                    return -5;
                }
            } else {
                //         *  -3 no caso do nivelMax ser superior ao definido
                return -3;
            }

        } else {
            //  *  -4 no caso do ficheiro não existir, erro 400/500

            return -4;
        }
        //*  0 tudo ok
        return 0;
    }

    //*************************************************************
    /*
     * set do tamanho do ficheiro do url
     */
    private void setSize() {
        this.size = this.getFileSize();
    }

    private String nomeFileTest(String fileName) {
        int idx = fileName.replaceAll("\\\\", "/").lastIndexOf("/");
        idx++;
        return idx >= 0 ? fileName.substring(idx) : fileName;
    }

    /*
     * set do fullWebPath
     */
    private void setFullWebPath(String webPath) {

        this.fullWebPath = webPath;
    }

    /*
     * define o host
     */
    private void setHost() {
        this.host = this.url.getHost();
    }

    /*
     * define o path local
     */
    private void setLocalPath(String localPath) {
        this.localPath = localPath;
    }

    /*
     * Este metodo verifica o contentType do ficheiro a descarregar 
     */
    private boolean fileType() {
        FileNameMap fileNameMap = URLConnection.getFileNameMap();

        try {
            String mimeType = fileNameMap.getContentTypeFor(this.fullWebPath);
            if (mimeType.equals("text/html")) {
                this.tipo = true;
                return true;

            } else {
                this.tipo = false;
                return false;

            }
        } catch (NullPointerException ex) {
          //  System.out.println("\n Erro ao obter o tipo de ficheiro " + ex.getMessage());
            return false;
        }

    }

    private void setProtocol() {
        this.protocol = this.url.getProtocol();
    }

    private String getProtocol() {
        return this.protocol;
    }

    //************************funções auxiliares************************
    /*
     * verifica o tamanho do ficheiro do url em bytes (int)
     * no caso de endereço errado ou inexistente retorna -1
     *      conn = url.openConnection();
     size = conn.getContentLength();
     */
    private int getFileSize() {
        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) this.url.openConnection();
            conn.setRequestMethod("HEAD");
            conn.getInputStream();

            return conn.getContentLength();

        } catch (IOException e) {
            System.out.println("\n Erro ao obter o tamanho do ficheiro " + e.getMessage());
            return -1;
        } finally {
            conn.disconnect();
        }
    }

    //verifica se é windows
    private void setWindows() {
        if (this.localPath.contains("\\")) {
            this.windows = true;
        } else {
            this.windows = false;
        }

    }

    private void setOnlyUrlPath() {

        /*
         * 1-retira o nome do ficheiro do url ao path
         */
      try {
            this.path = this.url.getPath().replaceAll(this.fileName, "");
       } catch (PatternSyntaxException e) {
           // System.out.println("\n Erro ao obter o tamanho do ficheiro " + e.getMessage());
            this.path=this.url.getPath();
        }

    }

    //substitui / por \ ou vice versa
    private String convertBarras(String str) {
        String tmp = null;
        if (str.contains("/") && this.isWindows()) {
            tmp = str.replace("/", "\\");
        } else {
            tmp = str;
        }
        return tmp;
    }

    private int coreDownloadFile() throws MalformedURLException, IOException {

        BufferedInputStream in = null;
        FileOutputStream fout = null;
        try {


            //criar ligação externa

            try {

                this.criaDir(this.getFullLocalPath());

                in = new BufferedInputStream(new URL(this.getFullWebPath()).openStream());
            } catch (IOException ex) {
              //  System.out.println("\nErro na criação da diretoria local ou abrir url");
                return -1;
            }

            //criação ficheiro local
            try {
                String tmp = this.getFullLocalPathFile();

                //cria directoria

                this.criaDir(this.getFullLocalPath());

                fout = new FileOutputStream(tmp);
            } catch (IOException ex) {
                System.out.println("\nErro na criação do ficheiro/directoria local " + ex.getMessage());
                return -2;
            }

            byte data[] = new byte[1024];
            int count;
            while ((count = in.read(data, 0, 1024)) != -1) {
                fout.write(data, 0, count);
            }

        } finally {
            if (in != null) {
                in.close();
            }
            if (fout != null) {
                fout.close();
            }
        }

        //verifica e atualiza o tamanho caso seja diferente
        tamanhoFilelocal();
        return 1;
    }

    /*
     * retorna o path local criado
     */
    private String getLocalPath() {
        return this.localPath;
    }

    //verifica e trata das / e \ no windows 
    private boolean isWindows() {
        return this.windows;
    }

    //set do nome do ficheiro
    private void setFileName(String fileName) {
        this.fileName = fileName;
    }

    //para verificar se o url está online
    private static int getResponseCode(String urlString) throws MalformedURLException, IOException {
        URL u = new URL(urlString);
        HttpURLConnection huc = (HttpURLConnection) u.openConnection();
        huc.setRequestMethod("GET");
        huc.connect();
        return huc.getResponseCode();
    }

    //cria directorias no disco
    private int criaDir(String dir) {
        File localDir = new File(dir);


        if (!localDir.exists()) {
            boolean success = (new File(dir)).mkdirs();
            if (!success) {
                return -2;
            }
            return 1;
        }
        return 1;
    }

    public void verificaNivel(int nivelMax) {
        int charCount = this.fullWebPath.replaceAll("[^/]", "").length();
        this.nivel = (charCount-2);
        if ((charCount - 2) <= nivelMax) {
            this.nivelMax = true;
        } else {
            this.nivelMax = false;
        }
    }

    private void verificaUltimoCaracterPathLocal() {
        String lastCaract = this.localPath.substring(this.localPath.length() - 1);

        if (lastCaract.compareTo("/") != 0 && this.localPath.contains("/")) {
            this.localPath = this.localPath + "/";
        } else if (lastCaract.compareTo("\\") != 0 && this.localPath.contains("\\")) {
            this.localPath = this.localPath + "\\";
        }
    }

    private void verificaURL() {
        /*
         * 1-ver comprimento do url
         * 2-posição da ultima /
         * 3-procurar por ponto entre a alinea 2 e 1
         * 4-se não existir, é porque temos um url tipo http://www.xx.pt/rr
         * 5-acrescentar / no final da str para que seja considerada a directoria e filename é index.html, type = true
         */

        int tamanhoURL = this.fullWebPath.length();
        int posBarra = this.fullWebPath.lastIndexOf("/");
        posBarra++;

        String part = this.fullWebPath.substring(posBarra, tamanhoURL);

        //no caso dos host tipo http://www.xx.com
        if (posBarra <= 8) {
            this.fullWebPath = this.fullWebPath + "/";
            this.path = "/";
        } else //no caso de ficheiros
        if (part.contains(".")) {
            this.setFileName(nomeFileTest(this.getFullWebPath()));
            setOnlyUrlPath();
            if (fileType()) { //verifica o tipo de ficheiro se para parser ou não
                this.tipo = true;
            } else {
                this.tipo = false;
            }

        } else //todos os restantes casos, ou seja directorias
        {
            this.fileName = "index.html";
            setOnlyUrlPath();
        }

    }
    //************************funções retorno************************
    /*
     * verificar se o tipo de url é para ser processado pelo parser
     */

    public boolean isPagetoParser() {
        return this.tipo;
    }

    /*
     * resultado -1 - falha ao obter ficheiro externo
     * resultado -2 - falha ao criar ficheiro local
     * resultado  1 - operação ok
     */
    //public int downloadFile(String path, String urlString) {
    public int downloadFile() {
        int resultado = 1;
        try {
            resultado = this.coreDownloadFile();
        } catch (MalformedURLException e) {
            System.out.println("\nfalha ao obter ficheiro externo " + e.getMessage());
            return -1;
        } catch (IOException ex) {
            System.out.println("\nfalha ao obter ficheiro externo " + ex.getMessage());
        }
        return resultado;
    }

    /*
     * retorna o path na web do url passado
     */
    public String getFullWebPath() {
        return this.fullWebPath;
    }

    /*
     * retorna o tamanho do ficheiro do url em bytes (int)
     * no caso de endereço errado ou inexistente retorna -1
     */
    public long getSize() {
        return size;
    }

    //retorna full path local 
    public String getFullLocalPath() {
        String tmp = this.getLocalPath();
        tmp += this.getHost();
        tmp += this.getPath();
        return tmp;
    }

    //retorna full path com file
    public String getFullLocalPathFile() {
        return getFullLocalPath() + this.getFileName();
    }

    /*
     * retorna o host/site d url
     */
    public String getHost() {

        return this.host;
    }

    /*
     * retorna o nome do ficheiro
     */
    public String getFileName() {
        return fileName;
    }

    /*
     * retorna o path
     */
    public String getPath() {
        return path;
    }

    //retorna o nome do ficheiro
    public String nomeFileHTML() {

        return this.fileName;
    }
    //*************************************************************

    private void ifOnlyHost() {
        int t = 0;
        if (this.getProtocol().length() == 4) {
            t = 7; //caso http://
        } else {
            t = 8; //caso https://
        }

        t += this.getHost().length();
        t += this.getPath().length();

        //caso o protocol + host + path ser = ao tamanho total do url, caso de nao ter ficheiro
        if (t == (this.getFullWebPath().length())) {
            this.setFileName("index.html");

            //verificar qual o ultimo caracter do url
            String lastCaract = this.getFullWebPath().substring(this.getFullWebPath().length() - 1);
            if (lastCaract.compareTo("/") != 0) {
                //colocar a /
                this.setFullWebPath(this.getFullWebPath() + "/");
                this.path += "/";
            }
            this.tipo = true;
        }

    }

    /*
     * //verifica e atualiza o tamanho caso seja diferente
     */
    public void tamanhoFilelocal() {
        File file = new File(this.getFullLocalPathFile());

        long byteSize = file.length();

        // Divide by 1024 to get size in KB  
        long kbSize = byteSize / 1024;

        //     System.out.println(byteSize + " bytes");
        if (this.size != byteSize) {
            this.size = byteSize;
        }
    }

    public int getNivel() {
        return nivel;
    }
}
